package bpce.object;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import plugin.bpce.modelo.DiagramaPublicado;
import plugin.bpce.modelo.ModeloAvaliado;

import bpce.modelo.EntradaLog;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class Log {	
	private String path;
	private DocumentBuilder build;
	private Document docLog;
	private Node noRaiz;
	private XPath xpath;

	public Log(String coppeerHome) {
		File tmp;
		DocumentBuilderFactory factory;	

		tmp = new File(coppeerHome + "/rep_BPCE/");
		if (!(tmp.exists()))
			tmp.mkdirs();
		
		path = coppeerHome + "/rep_BPCE/log.xml";
		
		try{
			xpath = XPathFactory.newInstance().newXPath();
			
		    factory = DocumentBuilderFactory.newInstance();
	        factory.setNamespaceAware(true);	         
	        build = factory.newDocumentBuilder();
	        
	        docLog = build.parse(tmp);			
			noRaiz = docLog.getDocumentElement();
		}
		catch(Exception e){
			geraArquivo();			
		}
	}

	private void geraArquivo() {
		DOMImplementation dom;		
		
		try{
			dom = build.getDOMImplementation();
			docLog= dom.createDocument(null, null, null);
			noRaiz = docLog.createElement("Log");						
			docLog.insertBefore(noRaiz, null);
			geraXML();
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	private void geraXML() {
        FileWriter writer;
        BufferedWriter buf;
        OutputFormat format;
        XMLSerializer output;
        
        try{
            writer = new FileWriter(this.path);
            buf = new BufferedWriter(writer);            
            format = new OutputFormat(this.docLog, "ISO-8859-1", true);
            format.setLineSeparator(System.getProperty("line.separator"));
            output = new XMLSerializer(buf, format);
            output.serialize(this.docLog);
        } catch(Exception e){
            e.printStackTrace();
        }
	}

	public boolean existeLog() {
		@SuppressWarnings("unused")
		Node no;
		boolean resultado;
		
		try{
			no = (Node) xpath.evaluate("//Log/CopiaModeladores", this.docLog, XPathConstants.NODE);
			if (no == null)
				resultado = false;
			else
				resultado = true;
		}
		catch(Exception e){
			resultado = false;
		}
		
		return resultado;
	}

	public void copiaModeladores(List<String> modeladores, Map<String, List<DiagramaPublicado>> mapaDiagramas) {
		Element noModelador, noDiagrama;
		int i, j;
		String nomeModelador;
		List<DiagramaPublicado> listaDiagramas;
		DiagramaPublicado diagrama;
		Node noCopiaModeladores;
	
		noCopiaModeladores = getNoCopiaModeladores();
		
		for(i = 0; i < modeladores.size(); i++){
			nomeModelador = modeladores.get(i);
			listaDiagramas = mapaDiagramas.get(nomeModelador);
			
			noModelador = this.docLog.createElement("Modelador");
			noModelador.setAttribute("Nome", nomeModelador);
			
			for(j = 0; j < listaDiagramas.size(); j++){
				diagrama = listaDiagramas.get(j);
				
				noDiagrama = this.docLog.createElement("Diagrama");
				noDiagrama.setAttribute("Agente", diagrama.getAgente());
				noDiagrama.setAttribute("Avaliou", diagrama.getAvaliou());
				noDiagrama.setAttribute("NomeDiagrama", diagrama.getNomeDiagrama());
				noDiagrama.setAttribute("PtoAvaliacao", diagrama.getPtoAvaliacao());
				noDiagrama.setAttribute("PtoInicial", diagrama.getPtoInicial());
				noDiagrama.setAttribute("PtoVisualizacao", diagrama.getPtoVisualizacao());
				
				noModelador.appendChild(noDiagrama);
			}
			
			noCopiaModeladores.appendChild(noModelador);
		}
		
		geraXML();
	}
	
	private Node getNoCopiaModeladores(){
		return getNo("CopiaModeladores");
	}

	public void geraLogPublicacao(String nomeAutor, String nomeDiagrama, String avaliou, List<ModeloAvaliado> listaModeloAvaliado, List<String> agentesDesconectados, long data) {
		Node noEntradas;
		Element noEntradaLog, noAgenteDesconectado, noModeloAvaliado;
		int i;
		ModeloAvaliado modelo;
		
		noEntradas = getNoEntradas();
		noEntradaLog = this.docLog.createElement("EntradaLog");
		noEntradaLog.setAttribute("Tipo", "1");
		noEntradaLog.setAttribute("NomeAutor", nomeAutor);
		noEntradaLog.setAttribute("NomeDiagrama", nomeDiagrama);
		noEntradaLog.setAttribute("Avaliou", avaliou);
		noEntradaLog.setAttribute("Data", Long.toString(data));
		
		for(i = 0; i < listaModeloAvaliado.size(); i++){
			modelo = listaModeloAvaliado.get(i);
			
			noModeloAvaliado = this.docLog.createElement("DiagramaUtilizado");
			noModeloAvaliado.setAttribute("NomeDiagrama", modelo.getNomeModelo());
			noModeloAvaliado.setAttribute("NomeModelador", modelo.getNomeAutor());
			noModeloAvaliado.setAttribute("Pontuacao", Integer.toString(modelo.getPontuacao()));
			
			noEntradaLog.appendChild(noModeloAvaliado);
		}
		
		for(i = 0; i < agentesDesconectados.size(); i++){
			noAgenteDesconectado = this.docLog.createElement("AgenteDesconectado");
			noAgenteDesconectado.setAttribute("NomeAgente", agentesDesconectados.get(i));
			
			noEntradaLog.appendChild(noAgenteDesconectado);
		}
		
		noEntradas.appendChild(noEntradaLog);
		geraXML();		
	}
	
	private Node getNoEntradas(){
		return getNo("Entradas");
	}
	
	private Node getNo(String nome){
		Node no;
		
		try{
			no = (Node) xpath.evaluate("//Log/" + nome, this.docLog, XPathConstants.NODE);
			
			if (no == null){
				no = this.docLog.createElement(nome);
				this.noRaiz.appendChild(no);
				geraXML();				
			}
		}
		catch(Exception e){
			e.printStackTrace();
			no = null;
		}
		
		return no;		
	}

	public void geraLogPontuacao(String nomeAutor, String nomeModelo, String nomePontuador, List<String> agentesDesconectados, Long data) {
		Node noEntradas;
		Element noEntradaLog, noAgenteDesconectado;
		int i;
		
		noEntradas = getNoEntradas();
		noEntradaLog = this.docLog.createElement("EntradaLog");
		noEntradaLog.setAttribute("NomeDiagrama", nomeModelo);
		noEntradaLog.setAttribute("NomeAutor", nomeAutor);
		noEntradaLog.setAttribute("NomePontuador", nomePontuador);
		noEntradaLog.setAttribute("Tipo", "2");
		noEntradaLog.setAttribute("Pontuacao", "1");
		noEntradaLog.setAttribute("Data", Long.toString(data));
		
		for(i = 0; i < agentesDesconectados.size(); i++){
			noAgenteDesconectado = this.docLog.createElement("AgenteDesconectado");
			noAgenteDesconectado.setAttribute("NomeAgente", agentesDesconectados.get(i));
			
			noEntradaLog.appendChild(noAgenteDesconectado);
		}
		
		noEntradas.appendChild(noEntradaLog);
		geraXML();
	}
	
	public List<String> getCopiaModeladores() {
		NodeList nlModeladores;
		int i;
		Element noModelador;
		List<String> retorno;
		
		nlModeladores = getNoModeladores();
		retorno = new ArrayList<String>();
		
		for(i = 0; i < nlModeladores.getLength(); i++){
			noModelador = (Element) nlModeladores.item(i);
			retorno.add(noModelador.getAttribute("Nome"));
		}
		
		return retorno;
	}
	
	private NodeList getNoModeladores(){
		NodeList nl;
		
		try{
			nl = (NodeList) xpath.evaluate("//Log/CopiaModeladores/Modelador" , this.docLog, XPathConstants.NODESET);
		}
		catch(Exception e){
			e.printStackTrace();
			nl = null;
		}
		
		return nl;		
	}
	
	public Map<String, List<DiagramaPublicado>> getCopiaDiagramas(){
		Map<String, List<DiagramaPublicado>> retorno;
		List<String> modeladores;
		NodeList nlDiagramas;
		int i, j;
		String nomeModelador;
		Element noDiagrama;
		List<DiagramaPublicado> listaDiagramas;
		DiagramaPublicado diagrama;
		
		retorno = new HashMap<String, List<DiagramaPublicado>>();
		modeladores = getCopiaModeladores();		
		
		for(i = 0; i < modeladores.size(); i++){
			nomeModelador = modeladores.get(i);
			nlDiagramas = getDiagramasModelador(nomeModelador);
			listaDiagramas = new ArrayList<DiagramaPublicado>();
			
			for(j = 0; j < nlDiagramas.getLength(); j++){
				noDiagrama = (Element) nlDiagramas.item(j);
				diagrama = new DiagramaPublicado();
				diagrama.setAgente(noDiagrama.getAttribute("Agente"));
				diagrama.setAvaliou(noDiagrama.getAttribute("Avaliou"));
				diagrama.setNomeDiagrama(noDiagrama.getAttribute("NomeDiagrama"));
				diagrama.setPtoAvaliacao(noDiagrama.getAttribute("PtoAvaliacao"));
				diagrama.setPtoInicial(noDiagrama.getAttribute("PtoInicial"));
				diagrama.setPtoVisualizacao(noDiagrama.getAttribute("PtoVisualizacao"));
				
				listaDiagramas.add(diagrama);
			}
			
			retorno.put(nomeModelador, listaDiagramas);
		}
		
		return retorno;
	}
	
	private NodeList getDiagramasModelador(String nomeModelador) {
		NodeList nlDiagramas;
		
		try{
			nlDiagramas = (NodeList) xpath.evaluate("//Log/CopiaModeladores/Modelador[@Nome='" + nomeModelador + "']/Diagrama", this.noRaiz, XPathConstants.NODESET);
		}
		catch(Exception e){
			nlDiagramas = null;
		}
		
		return nlDiagramas;
	}
	
	public List<EntradaLog> getLogs(){
		List<EntradaLog> listaLogs;
		NodeList nlLogs, nlAgentesDesconectados, nlModelosAvaliados;
		int i, j;
		Element noLog, noAgenteDesconectado, noModeloAvaliado;
		EntradaLog log;
		List<String> agentesDesconectados;
		List<ModeloAvaliado> listaModeloAvalliado;
		ModeloAvaliado modelo;
		
		listaLogs = new ArrayList<EntradaLog>();
		nlLogs = getNoLogs();
		
		for(i = 0; i < nlLogs.getLength(); i++){
			noLog = (Element) nlLogs.item(i);
			log = new EntradaLog();
			agentesDesconectados = new ArrayList<String>();
			
			log.setTipo(noLog.getAttribute("Tipo"));
			log.setNomeDiagrama(noLog.getAttribute("NomeDiagrama"));
			log.setNomeAutor(noLog.getAttribute("NomeAutor"));
			log.setData(noLog.getAttribute("Data"));
			
			nlAgentesDesconectados = noLog.getElementsByTagName("AgentesConhecidos");
			for(j = 0; j < nlAgentesDesconectados.getLength(); j++){
				noAgenteDesconectado = (Element) nlAgentesDesconectados.item(j);
				agentesDesconectados.add(noAgenteDesconectado.getAttribute("NomeAgente"));
			}
			
			log.setListaAgentesDesconectados(agentesDesconectados);
			
			if (noLog.getAttribute("Tipo").equalsIgnoreCase("1")){
				log.setAvaliou(noLog.getAttribute("Avaliou"));
				
				listaModeloAvalliado = new ArrayList<ModeloAvaliado>();
				modelo = new ModeloAvaliado();
				
				nlModelosAvaliados = noLog.getElementsByTagName("DiagramaUtilizado");				
				for(j = 0; j < nlModelosAvaliados.getLength(); j++){
					noModeloAvaliado = (Element) nlModelosAvaliados.item(j);
					modelo.setNomeAutor(noModeloAvaliado.getAttribute("NomeModelador"));
					modelo.setNomeModelo(noModeloAvaliado.getAttribute("NomeDiagrama"));
					modelo.setPontuacao(Integer.parseInt(noModeloAvaliado.getAttribute("Pontuacao")));
					
					listaModeloAvalliado.add(modelo);
				}
				
				log.setListaModeloAvaliado(listaModeloAvalliado);
			}
			else{
				log.setNomePontuador(noLog.getAttribute("NomePontuador"));
			}
			
			listaLogs.add(log);
		}
		
		return listaLogs;
	}
	
	private NodeList getNoLogs(){
		NodeList nlLogs;
		
		try{
			nlLogs = (NodeList) xpath.evaluate("//Log/Entradas/EntradaLog", this.noRaiz, XPathConstants.NODESET);
		}
		catch(Exception e){
			nlLogs = null;
		}
		
		return nlLogs;		
	}

	public void sincronizaLogs(List<EntradaLog> listaLogRecebido, List<String> listaCopiaModeladores, Map<String, List<DiagramaPublicado>> listaCopiaDiagramas, String nomeAgente) {
		List<EntradaLog> listaLogAtual;
		EntradaLog logAtual, logRecebido;
		
		listaLogAtual = getLogs();
	}
}
